Dedup auto-memoize passthrough definitions during compile#6735
Conversation
Greptile SummaryThis PR improves auto-memoized component compilation and update emission performance. The main changes are:
Confidence Score: 5/5This looks safe to merge.
Important Files Changed
Reviews (6): Last reviewed commit: "Update moment pyi hash: memo dedup chang..." | Re-trigger Greptile |
Merging this PR will improve performance by 27.63%
|
| Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|
| ⚡ | test_compile_page[_stateful_page] |
30.2 ms | 23 ms | +31.24% |
| ⚡ | test_compile_page_full_context[_stateful_page] |
34.7 ms | 27.5 ms | +26.24% |
| ⚡ | test_compile_all_artifacts[_stateful_page] |
26.6 ms | 21.2 ms | +25.47% |
| 🆕 | test_compile_page_duplicated_rows |
N/A | 647.2 ms | N/A |
Tip
Curious why this is faster? Comment @codspeedbot explain why this is faster on this PR, or directly use the CodSpeed MCP with your agent.
Comparing claude/reflex-compiler-perf-t8ztc9-11-memoize-dedup (dea5c0c) with claude/reflex-perf-optimizations-21kg8y-emit-update (34610c2)
Footnotes
-
8 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩
The auto-memoize plugin built a full memo definition for every stateful call site and only deduped by tag afterwards. Per component this ran _analyze_params twice on a constant signature (get_type_hints eval-compiles the stringized annotations each time) and evaluated the memo body twice. - Hoist the passthrough params analysis to an import-time constant; the wrapper signature (children: Var[Component]) never changes. - Reuse the evaluated preview as the definition body instead of re-running the memo function inside _create_component_definition. - Check CompileContext.auto_memo_components for the computed (tag, source_module) key before building, so repeated identical subtrees reuse the first definition instead of rebuilding an equivalent one per call site. Adds a duplicated-rows compile benchmark that stresses this path. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jy8uHH11KircGa2MbTrR8g
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jy8uHH11KircGa2MbTrR8g
create_passthrough_component_memo gained the existing_definitions parameter, changing the generated stub. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jy8uHH11KircGa2MbTrR8g
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jy8uHH11KircGa2MbTrR8g
…build
Hash the call-site component with a constant {children}-hole render to
derive the memo tag before any body work happens. On a registry hit the
plugin now skips the event-trigger rewrite and the memo body build
entirely; on a miss the new prepare_body hook applies the rewrite and
the definition self-registers.
Soundness of dedup-by-tag requires the component hash to cover every
input the memo module emission reads, so dynamic imports are now hashed
(with a regression test). The hash walk also gets exact-type fast paths
for str/dict/list and a per-class dataclass field-name cache.
0390d78 to
dea5c0c
Compare
All Submissions:
Type of change
Changes To Core Features:
What this does
The auto-memoize plugin (
MemoizeStatefulPlugin) built a full memo definition for every stateful call site and only deduped by(tag, source_module)afterwards. In profiling of a 500-duplicated-row page this was ~56% of compile CPU: 1,001 definitions were built and then collapsed to 3 unique ones. Per call site it:_analyze_paramstwice on a constant signature — withfrom __future__ import annotations,get_type_hintseval-compiles the stringized annotations each time (2,472builtins.compilecalls in the profile), and_create_component_definition).Fix (behavior-preserving)
_PASSTHROUGH_PARAMS): the passthrough wrapper signature is always(children: Var[Component]) -> Component._LazyBody.ready(_lift_rest_props(preview))) instead of re-running the memo function.CompileContext.auto_memo_componentsfor the computed(tag, source_module)key first (new optionalexisting_definitionsparameter): repeated identical subtrees reuse the first definition instead of rebuilding an equivalent one per call site. The cache stays per-CompileContext, so definitions are still never shared across compiles (covered bytest_passthrough_memo_definitions_are_not_shared_globally).Compile output is unchanged: the surviving definition is structurally identical to the one that previously overwrote the registry entry last.
Measured results (CodSpeed instrumentation, this PR vs
main@ 9a5c4d3)Overall: +21.96% compile performance.
BASEHEADtest_compile_all_artifacts[_stateful_page]test_compile_page[_stateful_page]test_compile_page_full_context[_stateful_page]test_compile_page_duplicated_rows(new)Verification
source_modulestill misses); the memo body is evaluated exactly once per definition build.test_compile_page_duplicated_rows(200 identical stateful rows) stresses this path so follow-up compiler-perf PRs are measured on every run.Part of a compiler-performance series; tracked in Linear as ENG-10101.
🤖 Generated with Claude Code
https://claude.ai/code/session_01Jy8uHH11KircGa2MbTrR8g